package com.example.lishop.util

class SnowflakeUtil {
    /**
     * 序列id所占的位数 支持每毫秒产生1024个id序号
     */
    private val sequenceBits = 10L
    /**
     * 机器id所占的位数 支持256台机器
     */
    private val workerIdBits = 8L
    /**
     * 区域id所占的位数 支持32个区域（即支持最大机器数为256*32=8192）
     */
    private val datacenterIdBits = 5L
    /**
     * 开始时间截 (2018-01-01 00:00:00) 可使用至2052年
     */
    private val twepoch = 3514736000000L
    /**
     * 机器id左移位
     */
    private val workerIdShift = sequenceBits
    /**
     * 区域id左移位
     */
    private val datacenterIdShift = sequenceBits + workerIdBits
    /**
     * 时间截左移位
     */
    private val timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits
    /**
     * 生成序列的掩码
     */
    private val sequenceMask = -1L xor (-1L shl sequenceBits.toInt())
    /**
     * 支持的最大机器id
     */
    private val maxWorkerId = -1L xor (-1L shl workerIdBits.toInt())
    /**
     * 支持的最大区域id
     */
    private val maxDatacenterId = -1L xor (-1L shl datacenterIdBits.toInt())
    /**
     * 当前毫秒内序列
     */
    private var sequence = 0L
    /**
     * 当前机器id
     */
    private var workerId: Long = 0
    /**
     * 当前区域id
     */
    private var datacenterId: Long = 0
    /**
     * 上次生成ID的时间截
     */
    private var lastTimestamp = -1L
    /**
     * 一台机子只需要一个实例，以保证产生有序的、不重复的ID
     */
//    private val instance: SnowflakeUtil = SnowflakeUtil()

//    constructor() { // 设置workerId和datacenterId
// TODO workerId和datacenterId可以通过数据库、配置文件、缓存等方式获取，这里为方便演示默认都设置为0
//        val workerId: Long = 0
//        val datacenterId: Long = 0
//        require(!(workerId > maxWorkerId || workerId < 0)) { String.format("workerId（０～%d）设置错误", maxWorkerId) }
//        require(!(datacenterId > maxDatacenterId || datacenterId < 0)) { String.format("datacenterId（０～%d）设置错误", maxDatacenterId) }
//        this.workerId = workerId
//        this.datacenterId = datacenterId
//    }

//    fun getInstance(): SnowflakeUtil? {
//        return instance
//    }

    companion object {
        val instance = SingletonHolder.holder
    }

    private object SingletonHolder {
        val holder= SnowflakeUtil()
    }

    /**
     * 获得下一个ID (该方法是线程安全的)
     *
     * @return SnowflakeId
     */
    @Synchronized
    fun nextId(tag:Long): Long {
        require(!(tag > maxDatacenterId || tag < 0)) { String.format("tag（０～%d）设置错误", maxDatacenterId) }
        this.datacenterId = tag

        var timestamp = timeGen()
        // 如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应当抛出异常
// 也就是说当应用运行时是不能将时钟改小的，要么异常退出，要么ID重复
        if (timestamp < lastTimestamp) {
            throw RuntimeException(String.format("系统时钟回退%d秒", lastTimestamp - timestamp))
        }
        // 如果是同一时间生成的，则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = sequence + 1 and sequenceMask
            // 毫秒内序列溢出
            if (sequence == 0L) { // 阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp)
            }
        } else {
            sequence = 0L
        }
        // 上次生成ID的时间截
        lastTimestamp = timestamp
        // 移位并通过或运算拼到一起组成64位的ID
        return (timestamp - twepoch shl timestampLeftShift.toInt()
                or (datacenterId shl datacenterIdShift.toInt())
                or (workerId shl workerIdShift.toInt())
                or sequence)
    }

    /**
     * 阻塞到下一个毫秒，直到获得新的时间戳
     *
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected fun tilNextMillis(lastTimestamp: Long): Long {
        var timestamp = timeGen()
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen()
        }
        return timestamp
    }

    /**
     * 返回以毫秒为单位的当前时间
     *
     * @return 当前时间(毫秒)
     */
    protected fun timeGen(): Long { // 考虑到当前系统时钟不准确以及修改时钟产生的ID问题，
// 这里可以根据自身业务使用网络时钟或其他更加准确及稳定的时钟
        return System.currentTimeMillis()
    }

}